﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using System.ComponentModel;

namespace PicSync
{
    class AsyncInfo
    {
        const int BUFFER_SIZE = 4096;

        public HttpWebRequest request = null;
        public HttpWebResponse response = null;
        public Stream stream = null;
        public byte[] buffer = new byte[BUFFER_SIZE];
        public StringBuilder contents = new StringBuilder();

    }

    class AsyncDownloadInfo
    {
        public string filename = null;
    }

    enum DownloadItemType
    {
        Directory = 0,
        Post = 1,
        Picture = 2,
    }

    // We use a file to store the tasks and log. Format like this:
    // TASK[3]:Post(Id=1276789513)
    // TASK[4]:Picture(Id=1276859228135872)
    // DONE[2]:175,189 bytes
    // FAILED[3]:404 NOT FOUND
    // FAILED[2]:403 FORBIDDEN
    // RETRY[4]
    // Note that we don't store directory page in the log file.
    // Because there is no id for directory, and the directory
    // changes constantly.
    class DownloadTask
    {
        public int TaskId;

        public DownloadItemType Type;
        public string Url; // the url to download
        public string Id;

        public override string ToString()
        {
            string s;
            switch (this.Type)
            {
                case DownloadItemType.Directory:
                    s = "Directory(Page=" + this.Id + ")";
                    break;
                case DownloadItemType.Post:
                    s = "Post(Id=" + this.Id + ")";
                    break;
                case DownloadItemType.Picture:
                    s = "Picture(Id=" + this.Id + ")";
                    break;
                default:
                    throw new ArgumentException();
            }
            return s;
        }

        public static DownloadTask Parse(string s)
        {
            Regex rgx = new Regex(@"(\w+)\(\w+=(.+)\)", RegexOptions.IgnoreCase);
            Match m = rgx.Match(s);
            if (!m.Success)
            {
                throw new ArgumentException();
            }

            DownloadTask task = new DownloadTask();
            string type = m.Groups[1].Value;
            string id = m.Groups[2].Value;
            switch (type.ToLowerInvariant())
            {
                case "picture":
                    task.Type = DownloadItemType.Picture;
                    break;
                case "post":
                    task.Type = DownloadItemType.Post;
                    break;
            }
            task.Id = id;
            return task;
        }
    }


    public class BoardReaderProgress
    {
        public string Message;
    }

    public class PostEntry
    {
        public string Title;
        public string Author;
        public string Date;

        public string Url;
        public string Id;
    }

    public class BoardReader
    {
        protected string m_boardName;

        List<DownloadTask> m_tasks = new List<DownloadTask>();
        // protected ;

        public string BoardName
        {
            get
            {
                return m_boardName;
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("BoardName");
                m_boardName = value;
            }
        }

        public void CancelAsync()
        {
        }

        protected string GetDirectoryUrl()
        {
            return "http://" + "bbs.sjtu.edu.cn/bbstdoc,board," + m_boardName + ".html";
        }

        public void ReadDirectory()
        {
            WebClient client = new WebClient();
            client.Headers.Add(
                @"User-Agent",
                @"Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)");
            Uri uri = new Uri(GetDirectoryUrl());

            DownloadTask task = new DownloadTask();
            task.Type = DownloadItemType.Directory;
            task.Url = GetDirectoryUrl();
            task.Id = null;

            client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(OnDownloadStringCompleted);
            client.Encoding = Encoding.GetEncoding("gbk");
            client.DownloadStringAsync(uri, task);

            //AsyncDownloadInfo ai = new AsyncDownloadInfo();
            //ai.filename = "tt1.txt";
            //client.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDownloadFileCompleted);
            //client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
            //client.DownloadFileAsync(uri, ai.filename, ai);
        }

        private void StartNextTask()
        {
        }

        private void OnDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            DownloadTask task = (DownloadTask)e.UserState;
            if (e.Error != null)
            {
                throw e.Error;
            }

            string html = e.Result;
            // parse html

            // remove the task from the task queue
            m_tasks.Remove(task);
            StartNextTask();
        }

        private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            // e.
 	        // throw new NotImplementedException();
        }

        private void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            AsyncDownloadInfo ai = (AsyncDownloadInfo)e.UserState;
            if (e.Error != null)
            {
                System.Windows.Forms.MessageBox.Show(e.Error.Message, "Error");
            }
        }

        //public PostEntry[] ParseDirectoryPageFromFile(string filename)
        //{
        //   using (StreamReader reader = new StreamReader(filename, Encoding.GetEncoding("gbk")))
        //    {
        //        string html = reader.ReadToEnd();
        //        return ParseDirectoryPage(html);
        //    }
        //}

        protected PostEntry[] ParseDirectoryPage(string html)
        {
            // Html format:
            // <tr><td>3605<td><td><a href="bbsqry?userid=wwllff">wwllff</a>
            //     <td>Jun 18 00:54
            //     <td><a href=bbstcon,board,PPPerson,reid,1276793656.html>○ 风哥出品 100618 给我爸最喜欢的阿根廷 #16P </a>
            // <tr>
            string pattern = @"<tr><td>\d+<td>.*?<a .*?>(.+?)</a>" +
                @".*?<td>(.+?)<td><a href=(bbstcon,board," +
                m_boardName + @",reid,(\d+)\.html)>○? ?(.*?)</a>";
            Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            MatchCollection matches = rgx.Matches(html);
            PostEntry[] dir = new PostEntry[matches.Count];
            for (int i = 0; i < matches.Count; i++)
            {
                Match m = matches[i];
                PostEntry ent = new PostEntry();
                ent.Author = m.Groups[1].Value;
                ent.Date = m.Groups[2].Value;
                ent.Url = m.Groups[3].Value;
                ent.Id = m.Groups[4].Value;
                ent.Title = m.Groups[5].Value;
                dir[i] = ent;
            }

            // Find next page
            string pattern2 = @"<a href=bbstdoc,board," + m_boardName +
                @",page,(\d+)\.html>上一页</a>";
            Regex rgx2 = new Regex(pattern2, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            Match match = rgx2.Match(html);
            if (match.Success)
            {
                string pageNumber = match.Groups[1].Value;
            }

            return dir;
        }

        // delegate 


        /*
        public void ReadDirectory()
        {
            WebClient client = new WebClient();
            string url = "http://bbs.sjtu.edu.cn/bbstdoc,board," + m_boardName + ".html";
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.AddRange(0);

            AsyncInfo ai = new AsyncInfo();
            ai.request = request;
            request.BeginGetResponse(new AsyncCallback(OnResponseReady), ai);
        }

        private void OnResponseReady(IAsyncResult arg)
        {
            AsyncInfo ai = (AsyncInfo)arg.AsyncState;
            ai.response = (HttpWebResponse)ai.request.EndGetResponse(arg);
            ai.stream = ai.response.GetResponseStream();
            ai.stream.BeginRead(ai.buffer, 0, ai.buffer.Length,
                new AsyncCallback(OnStreamReady), ai);
        }

        private void OnStreamReady(IAsyncResult arg)
        {
            AsyncInfo ai = (AsyncInfo)arg.AsyncState;

            int read = ai.stream.EndRead(arg);
            if (read > 0)
            {
                //ai.contents.Append(Encoding.GetEncoding("gbk").GetString(ai. myRequestState.BufferRead, 0, read));
                IAsyncResult asynchronousResult = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), myRequestState);
            }
            else
            {
                Console.WriteLine("\nThe contents of the Html page are : ");
                if (myRequestState.requestData.Length > 1)
                {
                    string stringContent;
                    stringContent = myRequestState.requestData.ToString();
                    Console.WriteLine(stringContent);
                }
                Console.WriteLine("Press any key to continue..........");
                Console.ReadLine();

                responseStream.Close();
                allDone.Set();

            }

        }
         * */
    }

    public class SjtuBoardReader : BoardReader
    {
    }

    public class SmthBoardReader : BoardReader
    {
    }

}
